﻿// author : David Janacek, dj-soft@seznam.cz, 2013-12-13
// company: Asseco Solution, a.s. CZ
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Noris.LCS.AIDCCodes.Common;

namespace Noris.LCS.AIDCCodes.QrCode
{
    // This file contain definitions of table containing specification of QR code, by ISO/IEC 18004 (Second edition 2006-09-01), INTERNATIONAL STANDARD (document: ISOIEC 18004_2006Cor 1_2009.pdf).
    // All entities have class for "collection" (i.e. QVersions), and class for "item" (i.e. QVersion).
    // Collection is autoinitiated (=fill its with all items), and has internal property = Dictionary with items.
    // Item has appropriate data property and small set of methods for support of encoding process.
    #region class QVersions: tabulka všech verzí. QVersion: jedna verze. QEcBlockSet: sada bloků. QEcBlock: jeden blok.
    /// <summary>
    /// QVersion: Tabulka všech verzí
    /// </summary>
    internal class QVersions : Table
    {
        #region Initiating
        /// <summary>
        /// Vytvoří sadu verzí. Každá verze obsahuje popis její definice.
        /// </summary>
        internal QVersions()
            : base()
        {
            this.VersionDict = new Dictionary<int, QVersion>();
            this.VersionDict.Add(1, new QVersion(1, 0, 4, "", "7;1;19", "10;1;16", "13;1;13", "17;1;9"));
            this.VersionDict.Add(2, new QVersion(2, 0, 4, "6;18", "10;1;34", "16;1;28", "22;1;22", "28;1;16"));
            this.VersionDict.Add(3, new QVersion(3, 0, 4, "6;22", "15;1;55", "26;1;44", "18;2;17", "22;2;13"));
            this.VersionDict.Add(4, new QVersion(4, 0, 4, "6;26", "20;1;80", "18;2;32", "26;2;24", "16;4;9"));
            this.VersionDict.Add(5, new QVersion(5, 0, 4, "6;30", "26;1;108", "24;2;43", "18;2;15;2;16", "22;2;11;2;12"));
            this.VersionDict.Add(6, new QVersion(6, 0, 4, "6;34", "18;2;68", "16;4;27", "24;4;19", "28;4;15"));
            this.VersionDict.Add(7, new QVersion(7, 0x07C94, 4, "6;22;38", "20;2;78", "18;4;31", "18;2;14;4;15", "26;4;13;1;14"));
            this.VersionDict.Add(8, new QVersion(8, 0x085BC, 4, "6;24;42", "24;2;97", "22;2;38;2;39", "22;4;18;2;19", "26;4;14;2;15"));
            this.VersionDict.Add(9, new QVersion(9, 0x09A99, 4, "6;26;46", "30;2;116", "22;3;36;2;37", "20;4;16;4;17", "24;4;12;4;13"));
            this.VersionDict.Add(10, new QVersion(10, 0x0A4D3, 4, "6;28;50", "18;2;68;2;69", "26;4;43;1;44", "24;6;19;2;20", "28;6;15;2;16"));
            this.VersionDict.Add(11, new QVersion(11, 0x0BBF6, 4, "6;30;54", "20;4;81", "30;1;50;4;51", "28;4;22;4;23", "24;3;12;8;13"));
            this.VersionDict.Add(12, new QVersion(12, 0x0C762, 4, "6;32;58", "24;2;92;2;93", "22;6;36;2;37", "26;4;20;6;21", "28;7;14;4;15"));
            this.VersionDict.Add(13, new QVersion(13, 0x0D847, 4, "6;34;62", "26;4;107", "22;8;37;1;38", "24;8;20;4;21", "22;12;11;4;12"));
            this.VersionDict.Add(14, new QVersion(14, 0x0E60D, 4, "6;26;46;66", "30;3;115;1;116", "24;4;40;5;41", "20;11;16;5;17", "24;11;12;5;13"));
            this.VersionDict.Add(15, new QVersion(15, 0x0F928, 4, "6;26;48;70", "22;5;87;1;88", "24;5;41;5;42", "30;5;24;7;25", "24;11;12;7;13"));
            this.VersionDict.Add(16, new QVersion(16, 0x10B78, 4, "6;26;50;74", "24;5;98;1;99", "28;7;45;3;46", "24;15;19;2;20", "30;3;15;13;16"));
            this.VersionDict.Add(17, new QVersion(17, 0x1145D, 4, "6;30;54;78", "28;1;107;5;108", "28;10;46;1;47", "28;1;22;15;23", "28;2;14;17;15"));
            this.VersionDict.Add(18, new QVersion(18, 0x12A17, 4, "6;30;56;82", "30;5;120;1;121", "26;9;43;4;44", "28;17;22;1;23", "28;2;14;19;15"));
            this.VersionDict.Add(19, new QVersion(19, 0x13532, 4, "6;30;58;86", "28;3;113;4;114", "26;3;44;11;45", "26;17;21;4;22", "26;9;13;16;14"));
            this.VersionDict.Add(20, new QVersion(20, 0x149A6, 4, "6;34;62;90", "28;3;107;5;108", "26;3;41;13;42", "30;15;24;5;25", "28;15;15;10;16"));
            this.VersionDict.Add(21, new QVersion(21, 0x15683, 4, "6;28;50;72;94", "28;4;116;4;117", "26;17;42", "28;17;22;6;23", "30;19;16;6;17"));
            this.VersionDict.Add(22, new QVersion(22, 0x168C9, 4, "6;26;50;74;98", "28;2;111;7;112", "28;17;46", "30;7;24;16;25", "24;34;13"));
            this.VersionDict.Add(23, new QVersion(23, 0x177EC, 4, "6;30;54;74;102", "30;4;121;5;122", "28;4;47;14;48", "30;11;24;14;25", "30;16;15;14;16"));
            this.VersionDict.Add(24, new QVersion(24, 0x18EC4, 4, "6;28;54;80;106", "30;6;117;4;118", "28;6;45;14;46", "30;11;24;16;25", "30;30;16;2;17"));
            this.VersionDict.Add(25, new QVersion(25, 0x191E1, 4, "6;32;58;84;110", "26;8;106;4;107", "28;8;47;13;48", "30;7;24;22;25", "30;22;15;13;16"));
            this.VersionDict.Add(26, new QVersion(26, 0x1AFAB, 4, "6;30;58;86;114", "28;10;114;2;115", "28;19;46;4;47", "28;28;22;6;23", "30;33;16;4;17"));
            this.VersionDict.Add(27, new QVersion(27, 0x1B08E, 4, "6;34;62;90;118", "30;8;122;4;123", "28;22;45;3;46", "30;8;23;26;24", "30;12;15;28;16"));
            this.VersionDict.Add(28, new QVersion(28, 0x1CC1A, 4, "6;26;50;74;98;122", "30;3;117;10;118", "28;3;45;23;46", "30;4;24;31;25", "30;11;15;31;16"));
            this.VersionDict.Add(29, new QVersion(39, 0x1D33F, 4, "6;30;54;78;102;126", "30;7;116;7;117", "28;21;45;7;46", "30;1;23;37;24", "30;19;15;26;16"));
            this.VersionDict.Add(30, new QVersion(30, 0x1ED75, 4, "6;26;52;78;104;130", "30;5;115;10;116", "28;19;47;10;48", "30;15;24;25;25", "30;23;15;25;16"));
            this.VersionDict.Add(31, new QVersion(31, 0x1F250, 4, "6;30;56;82;108;134", "30;13;115;3;116", "28;2;46;29;47", "30;42;24;1;25", "30;23;15;28;16"));
            this.VersionDict.Add(32, new QVersion(32, 0x209D5, 4, "6;34;60;86;112;138", "30;17;115", "28;10;46;23;47", "30;10;24;35;25", "30;19;15;35;16"));
            this.VersionDict.Add(33, new QVersion(33, 0x216F0, 4, "6;30;58;86;114;142", "30;17;115;1;116", "28;14;46;21;47", "30;29;24;19;25", "30;11;15;46;16"));
            this.VersionDict.Add(34, new QVersion(34, 0x228BA, 4, "6;34;62;90;118;146", "30;13;115;6;116", "28;14;46;23;47", "30;44;24;7;25", "30;59;16;1;17"));
            this.VersionDict.Add(35, new QVersion(35, 0x2379F, 4, "6;30;54;78;102;126;150", "30;12;121;7;122", "28;12;47;26;48", "30;39;24;14;25", "30;22;15;41;16"));
            this.VersionDict.Add(36, new QVersion(36, 0x24B0B, 4, "6;24;50;76;102;128;154", "30;6;121;14;122", "28;6;47;34;48", "30;46;24;10;25", "30;2;15;64;16"));
            this.VersionDict.Add(37, new QVersion(37, 0x2542E, 4, "6;28;54;80;106;132;158", "30;17;122;4;123", "28;29;46;14;47", "30;49;24;10;25", "30;24;15;46;16"));
            this.VersionDict.Add(38, new QVersion(38, 0x26A64, 4, "6;32;58;84;110;136;162", "30;4;122;18;123", "28;13;46;32;47", "30;48;24;14;25", "30;42;15;32;16"));
            this.VersionDict.Add(39, new QVersion(39, 0x27541, 4, "6;26;54;82;110;138;166", "30;20;117;4;118", "28;40;47;7;48", "30;43;24;22;25", "30;10;15;67;16"));
            this.VersionDict.Add(40, new QVersion(40, 0x28C69, 4, "6;30;58;86;114;142;170", "30;19;118;6;119", "28;18;47;31;48", "30;34;24;34;25", "30;20;15;61;16"));

            this.VersionList = new List<QVersion>(this.VersionDict.Values);
            this.VersionList.Sort((a, b) => a.VersionNumber.CompareTo(b.VersionNumber));
        }
        #endregion
        #region Data property
        internal Dictionary<int, QVersion> VersionDict { get; private set; }
        internal List<QVersion> VersionList { get; private set; }
        #endregion
    }
    /// <summary>
    /// Data jedné verze
    /// </summary>
    internal class QVersion : Table
    {
        #region Initiating
        internal QVersion(int versionNumber, int versionDecodeInfo, int quietZone , string alignmentPatternCenters, string ecBlocksL, string ecBlocksM, string ecBlocksQ, string ecBlocksH)
        {
            this.VersionNumber = versionNumber;
            this.VersionDecodeInfo = versionDecodeInfo;
            this.QuietZone = quietZone;
            this.AlignmentPatternCenters = Parse(alignmentPatternCenters);
            this.EcBlocks = new Dictionary<ErrorCorrectionLevel, QEcBlockSet>();
            this.EcBlocks.Add(ErrorCorrectionLevel.Low, new QEcBlockSet(ErrorCorrectionLevel.Low, ecBlocksL));
            this.EcBlocks.Add(ErrorCorrectionLevel.Medium, new QEcBlockSet(ErrorCorrectionLevel.Medium, ecBlocksM));
            this.EcBlocks.Add(ErrorCorrectionLevel.Quartile, new QEcBlockSet(ErrorCorrectionLevel.Quartile, ecBlocksQ));
            this.EcBlocks.Add(ErrorCorrectionLevel.High, new QEcBlockSet(ErrorCorrectionLevel.High, ecBlocksH));

            this.TotalCodewords = this.EcBlocks[ErrorCorrectionLevel.Low].TotalCodewords;

            this.PrepareMatrixData();
        }
        /// <summary>
        /// Vizualizace
        /// </summary>
        /// <returns></returns>
        public override string ToString()
        {
            return "Version: " + this.VersionNumber.ToString();
        }
        #endregion
        #region Data property
        /// <summary>
        /// Version number 1 ÷ 40
        /// </summary>
        internal int VersionNumber { get; private set; }
        /// <summary>
        /// Version code
        /// </summary>
        internal int VersionDecodeInfo { get; private set; }
        /// <summary>
        /// Quiet zone about QR code (on every side)
        /// </summary>
        internal int QuietZone { get; private set; }
        /// <summary>
        /// Pozice středů alignment vzorů (pomocné menší čtverce)
        /// </summary>
        internal int[] AlignmentPatternCenters { get; private set; }
        /// <summary>
        /// Rozměr daný číslem verze (Dimension = 17 + (4 * VersionNumber))
        /// </summary>
        internal int DimensionForVersion { get { return 17 + 4 * this.VersionNumber; } }
        /// <summary>
        /// Sada bloků pro různé typy korekce
        /// </summary>
        internal Dictionary<ErrorCorrectionLevel, QEcBlockSet> EcBlocks { get; private set; }
        /// <summary>
        /// Celkový počet Codewords určený pro ErrorCorrectionLevel.Low
        /// </summary>
        internal int TotalCodewords { get; private set; }
        #endregion
        #region Matrix property and prepare
        /// <summary>
        /// Prepare constants matrix data
        /// </summary>
        private void PrepareMatrixData()
        {
            int matrixWidth = this.DimensionForVersion;         // Number of point in X and Y direction
            int last = matrixWidth - 1;                         // Index of last coordinates right; down

            // Detection Center Coordinates:
            this.MatrixDetectionCenterCoords = new Coord[] { new Coord(3, 3), new Coord(last - 3, 3), new Coord(3, last - 3) };

            // Alignment Pattern Center Coordinates:
            List<Coord> acc = new List<Coord>();
            int[] apc = this.AlignmentPatternCenters;
            int apcCnt = apc.Length;
            for (int y = 0; y < apcCnt; y++)
                for (int x = 0; x < apcCnt; x++)
                    acc.Add(new Coord(apc[x], apc[y]));
            this.MatrixAdjustmentCenterCoords = acc.ToArray();

            this.MatrixHorizontalSeparatorCoords = new Coord[] { new Coord(0, 7), new Coord(last - 7, 7), new Coord(0, last - 7) };
            this.MatrixVerticalSeparatorCoords = new Coord[] { new Coord(7, 0), new Coord(last - 7, 0), new Coord(7, last - 6) };
            this.MatrixLeftBottomPointCoord = new Coord(8, last - 7);

            int timingLen = matrixWidth - 16;
            this.MatrixHorizontalTimingCoord = new Coord(8, 6);
            this.MatrixHorizontalTimingMatrix = new CodeMatrix(RepeatToLength("10", timingLen));
            this.MatrixVerticalTimingCoord = new Coord(6, 8);
            this.MatrixVerticalTimingMatrix = new CodeMatrix(RepeatToLength("1;0;", (2 * timingLen - 1)));

            this.MatrixTypeInfoSet1Coords = new Coord[]
            {
                new Coord(8, 0), new Coord(8, 1), new Coord(8, 2), new Coord(8, 3),
                new Coord(8, 4), new Coord(8, 5), new Coord(8, 7), new Coord(8, 8),
                new Coord(7, 8), new Coord(5, 8), new Coord(4, 8), new Coord(3, 8),
                new Coord(2, 8), new Coord(1, 8), new Coord(0, 8)
            };
            this.MatrixTypeInfoSet2Coords = new Coord[]
            {
                new Coord(last, 8),     new Coord(last - 1, 8), new Coord(last - 2, 8), new Coord(last - 3, 8),
                new Coord(last - 4, 8), new Coord(last - 5, 8), new Coord(last - 6, 8), new Coord(last - 7, 8),
                new Coord(8, last - 6), new Coord(8, last - 5), new Coord(8, last - 4), new Coord(8, last - 3),
                new Coord(8, last - 2), new Coord(8, last - 1), new Coord(8, last - 0)
            };
        }
        /// <summary>
        /// Return a string created from pattern, repeated to required length
        /// </summary>
        /// <param name="pattern"></param>
        /// <param name="length"></param>
        /// <returns></returns>
        private string RepeatToLength(string pattern, int length)
        {
            StringBuilder sb = new StringBuilder();
            while (sb.Length < length)
                sb.Append(pattern);
            if (sb.Length > length)
                sb.Remove(length, sb.Length - length);
            return sb.ToString();
        }
        /// <summary>Coordinates of center points of detection patterns (3 big squares)</summary>
        internal Coord[] MatrixDetectionCenterCoords { get; private set; }
        /// <summary>Coordinates of center points of adjustment patterns (small squares)</summary>
        internal Coord[] MatrixAdjustmentCenterCoords { get; private set; }
        /// <summary>Coordinates of first points of all horizontal separator patterns (white lines)</summary>
        internal Coord[] MatrixHorizontalSeparatorCoords { get; private set; }
        /// <summary>Coordinates of first points of all vertical separator patterns (white lines)</summary>
        internal Coord[] MatrixVerticalSeparatorCoords { get; private set; }
        /// <summary>Coordinates of point with value 1 near left-bottom detection point</summary>
        internal Coord MatrixLeftBottomPointCoord { get; private set; }
        /// <summary>Coordinates of first point of horizontal timing pattern (one horizontal dots line)</summary>
        internal Coord MatrixHorizontalTimingCoord { get; private set; }
        /// <summary>Matrix with horizontal timing pattern (one horizontal dots line)</summary>
        internal CodeMatrix MatrixHorizontalTimingMatrix { get; private set; }
        /// <summary>Coordinates of first point of vertical timing pattern (one vertical dots line)</summary>
        internal Coord MatrixVerticalTimingCoord { get; private set; }
        /// <summary>Matrix with vertical timing pattern (one vertical dots line)</summary>
        internal CodeMatrix MatrixVerticalTimingMatrix { get; private set; }
        /// <summary>Coordinates of points for Set 1 for Type an MaskPattern</summary>
        internal Coord[] MatrixTypeInfoSet1Coords { get; private set; }
        /// <summary>Coordinates of points for Set 2 for Type an MaskPattern</summary>
        internal Coord[] MatrixTypeInfoSet2Coords { get; private set; }

        /// <summary>Coordinates for matrix with version number, set 1 (bottom left). Matrix and coords are prepared in method this.PrepareVersionInfo().</summary>
        internal Coord MatrixVersionInfoSet1Coord { get; private set; }
        /// <summary>Matrix with version number, set 1 (bottom left). Matrix and coords are prepared in method this.PrepareVersionInfo().</summary>
        internal CodeMatrix MatrixVersionInfoSet1 { get; private set; }
        /// <summary>Coordinates for matrix with version number, set 2 (top right). Matrix and coords are prepared in method this.PrepareVersionInfo().</summary>
        internal Coord MatrixVersionInfoSet2Coord { get; private set; }
        /// <summary>Matrix with version number, set 2 (top right). Matrix and coords are prepared in method this.PrepareVersionInfo().</summary>
        internal CodeMatrix MatrixVersionInfoSet2 { get; private set; }
        /// <summary>true, if this version has area with version number</summary>
        internal bool IsVersionWithVersionInfo { get { return (this.VersionNumber >= 7); } }
        /// <summary>true, if this version has area with version number</summary>
        internal bool IsPreparedVersionInfo { get { return (this.MatrixVersionInfoSet1 != null && this.MatrixVersionInfoSet2 != null); } }
        /// <summary>
        /// Prepare values to property for paint { matrix and coords info } for this VersionNumber.
        /// </summary>
        /// <param name="versionPattern"></param>
        internal void PrepareVersionInfo(string versionPattern)
        {
            if (versionPattern.Length != 18)
                throw new ArgumentOutOfRangeException("versionPattern", "QVersion.PrepareVersionInfo(): versionPattern does not have length = 18.");

            if (this.IsVersionWithVersionInfo)
            {
                int begin = this.DimensionForVersion - 11;              // where matrix begin (on X or Y axis)

                this.MatrixVersionInfoSet1Coord = new Coord(0, begin);
                PatternBuffer buffer1 = PatternBuffer.CreateBufferScrambled(versionPattern + ";",
                    17, 14, 11, 8, 5, 2, 18,
                    16, 13, 10, 7, 4, 1, 18,
                    15, 12, 9, 6, 3, 0);
                this.MatrixVersionInfoSet1 = new CodeMatrix(buffer1.BufferString);

                this.MatrixVersionInfoSet2Coord = new Coord(begin, 0);
                PatternBuffer buffer2 = PatternBuffer.CreateBufferScrambled(versionPattern + ";",
                    17, 16, 15, 18,
                    14, 13, 12, 18,
                    11, 10, 9, 18,
                    8, 7, 6, 18,
                    5, 4, 3, 18,
                    2, 1, 0
                    );
                this.MatrixVersionInfoSet2 = new CodeMatrix(buffer2.BufferString);
            }
            else
            {   // Versions 1 ÷ 6: prepare dummy objects:
                this.MatrixVersionInfoSet1Coord = new Coord(0, 0);
                this.MatrixVersionInfoSet1 = new CodeMatrix(1, 1);
                this.MatrixVersionInfoSet2Coord = new Coord(0, 0);
                this.MatrixVersionInfoSet2 = new CodeMatrix(1, 1);
            }
        }
        #endregion
        #region Data searching
        /// <summary>
        /// Get QEcBlockSet instance for given ErrorCorrectionLevel
        /// </summary>
        /// <param name="ecLevel"></param>
        /// <returns></returns>
        internal QEcBlockSet GetEcBlockSet(ErrorCorrectionLevel ecLevel)
        {
            return this.EcBlocks[ecLevel];
        }
        /// <summary>
        /// Get number of data Byte for this version and specified ErrorCorrectionLevel.
        /// Data Byte count = this.TotalCodewords - (EcBlocks[ecLevel].TotalECCodewords);
        /// </summary>
        /// <param name="ecLevel"></param>
        /// <returns></returns>
        internal int GetDataByteCountForEcLevel(ErrorCorrectionLevel ecLevel)
        {
            // In the following comments, we use values of Version 7-H.
            int numBytes = this.TotalCodewords;                      // numBytes = 196
            QEcBlockSet ecBlocks = this.EcBlocks[ecLevel];           // EC block set for error correction
            int numEcBytes = ecBlocks.TotalECCodewords;              // getNumECBytes = 130
            int numDataBytes = numBytes - numEcBytes;                // getNumDataBytes = 196 - 130 = 66
            return numDataBytes;
        }
        #endregion
    }
    /// <summary>
    /// Popis sady bloků QR kódu v konkrétní verzi, pro konkrétní ErrorCorrectionLevel.
    /// Encapsulates a set of error-correction blocks in one symbol version. Most versions will
    /// use blocks of differing sizes within one version, so, this encapsulates the parameters for
    /// each set of blocks. It also holds the number of error-correction codewords per block since it
    /// will be the same across all blocks within one version.
    /// </summary>
    internal class QEcBlockSet : Table
    {
        #region Initiating
        internal QEcBlockSet(ErrorCorrectionLevel ecLevel, string definition)
        {
            this.EcLevel = ecLevel;
            int[] values = Parse(definition);
            int count = values.Length;
            if (!(count == 3 || count == 5))
                throw new ArgumentException("QEcBlockSet.ctor(definition) is not valid, contain " + count.ToString() + " items, valid count = 3 or 5.", "definition");

            this.ECCodewordsPerBlock = values[0];
            this.Blocks = new List<QEcBlock>();
            this.Blocks.Add(new QEcBlock(values[1], values[2]));
            if (count == 5)
                this.Blocks.Add(new QEcBlock(values[3], values[4]));

            this.NumBlocks = this.Blocks.Sum(qb => qb.Count);
            this.TotalECCodewords = this.ECCodewordsPerBlock * this.NumBlocks;
            this.TotalCodewords = this.Blocks.Sum(qb => qb.Count * (qb.DataCodewords + this.ECCodewordsPerBlock));
        }
        /// <summary>
        /// Vizualizace
        /// </summary>
        /// <returns></returns>
        public override string ToString()
        {
            return "EcLevel=" + this.EcLevel.ToString() + "; ECCodewordsPerBlock=" + this.ECCodewordsPerBlock.ToString() + "; BlocksCount=" + this.Blocks.Count.ToString();
        }
        #endregion
        #region Data property
        /// <summary>ErrorCorrectionLevel</summary>
        internal ErrorCorrectionLevel EcLevel { get; private set; }
        /// <summary></summary>
        internal int ECCodewordsPerBlock { get; private set; }
        /// <summary>1 nebo 2 instance třídy QEcBlock</summary>
        internal List<QEcBlock> Blocks { get; private set; }
        /// <summary>= Sum(this.Blocks.Count)</summary>
        internal int NumBlocks { get; private set; }
        /// <summary>= ECCodewordsPerBlock * NumBlocks</summary>
        internal int TotalECCodewords { get; private set; }
        /// <summary>= Sum(this.Blocks.Count * (this.Blocks.DataCodewords + this.ECCodewordsPerBlock))</summary>
        internal int TotalCodewords { get; private set; }
        #endregion
    }
    /// <summary>
    /// Specifikace jednoho bloku (obsahuje Count a DataCodewords).
    /// Encapsualtes the parameters for one error-correction block in one symbol version.
    /// This includes the number of data codewords, and the number of times a block with these
    /// parameters is used consecutively in the QR code version's format.
    /// </summary>
    internal class QEcBlock : Table
    {
        #region Initiating
        internal QEcBlock(int count, int dataCodewords)
        {
            this.Count = count;
            this.DataCodewords = dataCodewords;
        }
        /// <summary>
        /// Vizualizace
        /// </summary>
        /// <returns></returns>
        public override string ToString()
        {
            return "Count=" + this.Count.ToString() + "; DataCodewords=" + this.DataCodewords.ToString();
        }
        #endregion
        #region Data property
        internal int Count { get; private set; }
        internal int DataCodewords { get; private set; }
        #endregion
    }
    #endregion
    #region class QModes: tabulka všech režimů. QMode: jeden režim.
    /// <summary>
    /// Tabulka všech režimů
    /// </summary>
    internal class QModes : Table
    {
        #region Initiating
        internal QModes()
        {
            this.QModeDict = new Dictionary<QModeType, QMode>();
            this._AddQMode(QModeType.Terminator, "TERMINATOR", 0x00, new int[] { 0, 0, 0 });

            // Numeric mode encodes data from the decimal digit set (0 - 9) (byte values 0x30 to 0x39). Normally, 3 data characters are represented by 10 bits.
            this._AddQMode(QModeType.Numeric, "NUMERIC", 0x01, new int[] { 10, 12, 14 });

            // Alphanumeric mode encodes data from a set of 45 characters, i.e. 10 numeric digits (0 - 9) (byte values 0x30 to 0x39), 26 alphabetic characters (A - Z) (byte values 0x41 to 0x5A) , and 9 symbols (SP, $, %, *, +, -, ., /, :) (byte values 0x20, 0x24, 0x25, 0x2A, 0x2B, 0x2D , 0x2E, 0x2F, 0x3A respectively). Normally, two input characters are represented by 11 bits.
            this._AddQMode(QModeType.Alphanumeric, "ALPHANUMERIC", 0x02, new int[] { 9, 11, 13 });

            this._AddQMode(QModeType.StructuredAppend, "STRUCTURED_APPEND", 0x03, new int[] { 0, 0, 0 });

            // In this mode, data is encoded at 8 bits per character.
            this._AddQMode(QModeType.Byte, "BYTE", 0x04, new int[] { 8, 16, 16 });
            this._AddQMode(QModeType.Fnc1FirstPosition, "FNC1_FIRST_POSITION", 0x05, null);
            this._AddQMode(QModeType.Eci, "ECI", 0x07, null);
            this._AddQMode(QModeType.Kanji, "KANJI", 0x08, new int[] { 8, 10, 12 });
            this._AddQMode(QModeType.Fnc1SecondPosition, "FNC1_SECOND_POSITION", 0x09, null);
        }
        /// <summary>
        /// Create a new QMode from parameters and add it into this.QModeDict
        /// </summary>
        /// <param name="mode"></param>
        /// <param name="name"></param>
        /// <param name="bits"></param>
        /// <param name="counts"></param>
        private void _AddQMode(QModeType mode, string name, int bits, int[] counts)
        {
            QMode qMode = new QMode(mode, name, bits, counts);
            this.QModeDict.Add(mode, qMode);
        }
        #endregion
        #region Data property
        /// <summary>
        /// Collection of QMode indexed by QModeType
        /// </summary>
        internal Dictionary<QModeType, QMode> QModeDict { get; private set; }
        #endregion
    }
    /// <summary>
    /// Data jednoho režimu enkódování dat
    /// </summary>
    internal class QMode : Table
    {
        #region Initiating
        internal QMode(QModeType mode, string name, int bits, int[] counts)
        {
            this.Mode = mode;
            this.Name = name;
            this.Bits = bits;
            this.CharacterCountBitsForVersions = counts;
            this.EncodeMode = QModeEncodeMode.None;
            switch (mode)
            {
                case QModeType.Numeric:
                    this.EncodeMode = QModeEncodeMode.SubSet;
                    this.CodeTable = QCodeTable.CreateExplicitTable("0123456789", 0);
                    break;
                case QModeType.Alphanumeric:
                    this.EncodeMode = QModeEncodeMode.SubSet;
                    this.CodeTable = QCodeTable.CreateExplicitTable("0123456789" + "ABCDEFGHIJKLMNOPQRSTUVWXYZ" + @" $%*+-./:", 0);
                    break;
                case QModeType.Byte:
                    this.EncodeMode = QModeEncodeMode.BasicAscii;
                    this.CodeTable = QCodeTable.CreateBasicASCII();
                    break;
                case QModeType.Eci:
                    this.EncodeMode = QModeEncodeMode.Extended;
                    break;
            }
        }
        public override string ToString()
        {
            return this.Name;
        }
        #endregion
        #region Data property
        internal string Name { get; private set; }
        internal QModeType Mode { get; private set; }
        internal int Bits { get; private set; }
        internal int[] CharacterCountBitsForVersions { get; private set; }
        /// <summary>Režim kódování datových znaků</summary>
        internal QModeEncodeMode EncodeMode { get; private set; }
        /// <summary>CodeTable for encoding characters with this mode, can be null for modes, which can not directly encode data. Is prepared for mode NUMERIC, ALPHANUMERIC, BYTE.</summary>
        internal QCodeTable CodeTable { get; private set; }
        #endregion
        #region Encoding of content by mode (and preparing of codetable)
        /// <summary>
        /// Can this QMode encode given char?
        /// char has code 0x00 ÷ 0x7F.
        /// </summary>
        /// <param name="c"></param>
        /// <returns></returns>
        internal bool CanEncode(char c)
        {
            if (this.CodeTable == null) return false;
            return this.CodeTable.CanEncode(c);
        }
        /// <summary>
        /// Encode given content with this mode (and charset) to buffer
        /// </summary>
        /// <param name="content"></param>
        /// <returns></returns>
        internal void EncodeDataToBuffer(string content, QCharSet qCharSet, PatternBuffer buffer)
        {
            if (content == null)
                throw new ArgumentNullException("content", "QMode.Encode() can not encode content = NULL.");

            switch (this.Mode)
            {
                case QModeType.Numeric: 
                    this._EncodeDataToBufferNumeric(content, qCharSet, buffer);
                    break;
                case QModeType.Alphanumeric:
                    this._EncodeDataToBufferAlphanumeric(content, qCharSet, buffer);
                    break;
                case QModeType.Byte: 
                    this._EncodeDataToBufferByte(content, qCharSet, buffer);
                    break;
                default:
                    throw new InvalidOperationException("QMode \"" + this.Name + "\" can not encode any content.");
            }
        }
        /// <summary>
        /// Encode given content with mode NUMERIC
        /// </summary>
        /// <param name="content"></param>
        /// <returns></returns>
        private void _EncodeDataToBufferNumeric(string content, QCharSet qCharSet, PatternBuffer buffer)
        {
            QCodeTable table = this.CodeTable;
            int len = content.Length;
            int i3 = len - 2;
            int i2 = len - 1;
            try
            {
                for (int i = 0; i < len; i++)
                {
                    if (i < i3)
                    {   // 3 char to one pattern: append value i.e.: "245" => (100 * 2) + (10 * 4) + 5 = 245 (= 0x00F5 = 0011110101) to pattern with 10bit length => "1010111100":
                        int value = (100 * table.Encode(content[i])) + (10 * table.Encode(content[i + 1])) + table.Encode(content[i + 2]);
                        buffer.Append(value, 10);
                        i++;
                        i++;
                    }
                    else if (i < i2)
                    {   // 2 char to one pattern: append value i.e.: "87" => (10 * 8) + 7 = 87 (= 0x0057 = 1010111) to pattern with 7bit length => "1110101":
                        int value = (10 * table.Encode(content[i])) + table.Encode(content[i + 1]);
                        buffer.Append(value, 7);
                        i++;
                    }
                    else
                    {   // 1 char to one pattern: append value i.e.: "3" => 3 (= 0x0003 = 0011) to pattern with 4bit length => "1100":
                        int value = table.Encode(content[i]);
                        buffer.Append(value, 4);
                    }
                }
            }
            catch (System.Collections.Generic.KeyNotFoundException exc)
            {   // char (x) is not in codeTable
                throw new ArgumentOutOfRangeException("content", "QMode[NUMERIC].Encode(): data contain not expected values in string: \"" + content + "\". Info: " + exc.Message);
            }
        }
        /// <summary>
        /// Encode given content with mode ALPHANUMERIC
        /// </summary>
        /// <param name="content"></param>
        /// <returns></returns>
        private void _EncodeDataToBufferAlphanumeric(string content, QCharSet qCharSet, PatternBuffer buffer)
        {
            QCodeTable table = this.CodeTable;
            int len = content.Length;
            int i2 = len - 1;
            try
            {
                for (int i = 0; i < len; i++)
                {
                    if (i < i2)
                    {   // 2 char to one pattern: append value i.e.: "CX" => (45 * 12) + 33 = 573 (= 0x023D = 01000111101) to pattern with 11bit length => "10111100010":
                        int value = (45 * table.Encode(content[i])) + table.Encode(content[i + 1]);
                        buffer.Append(value, 11);
                        i++;
                    }
                    else
                    {   // 1 char to one pattern: append value i.e.: "%" => 48 (= 0x0030 = 110000) to pattern with 6bit length => "000011":
                        int value = table.Encode(content[i]);
                        buffer.Append(value, 6);
                    }
                }
            }
            catch (System.Collections.Generic.KeyNotFoundException)
            {
                throw new ArgumentOutOfRangeException("content", "QMode[ALPHANUMERIC].Encode(): data contain not expected values in string: \"" + content + "\".");
            }
            // table: "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ $%*+-./:"
            // index:  012345678901234567890123456789012345678901234
        }
        /// <summary>
        /// Encode given content with mode BYTE
        /// </summary>
        /// <param name="content"></param>
        /// <returns></returns>
        private void _EncodeDataToBufferByte(string content, QCharSet qCharSet, PatternBuffer buffer)
        {
            int len = content.Length;
            try
            {
                for (int i = 0; i < len; i++)
                {
                    char c = content[i];
                    int value = (int)content[i];
                    if (value <= 0x0080)
                        this.CodeTable.EncodeToBuffer(c, buffer);
                    else
                        qCharSet.CodeTable.EncodeToBuffer(c, buffer);
                }
            }
            catch (System.Collections.Generic.KeyNotFoundException)
            {
                throw new ArgumentOutOfRangeException("content", "QMode[BYTE].Encode(): data contain not expected values in string: \"" + content + "\".");
            }
        }
        #endregion
        #region Encoding of header
        /// <summary>
        /// Encode header from qCharSet and from this to new buffer
        /// </summary>
        /// <param name="qCharSet"></param>
        /// <returns></returns>
        internal void EncodeHeaderToBuffer(PatternBuffer buffer)
        {
            buffer.Append(this.Bits, 4);
        }
        #endregion
        #region Data searching
        /// <summary>
        /// Return count of bits for encode count of data character.
        /// </summary>
        /// <param name="qVersion"></param>
        /// <returns></returns>
        internal int GetCharacterCountBitsForVersion(QVersion qVersion)
        {
            if (qVersion.VersionNumber <= 9) return this.CharacterCountBitsForVersions[0];      // For versions up to 9 is bit count in array[0]
            if (qVersion.VersionNumber <= 26) return this.CharacterCountBitsForVersions[1];     // For versions up to 26 is bit count in array[1], else in array[2]:
            return this.CharacterCountBitsForVersions[2];
        }
        #endregion
    }
    /// <summary>
    /// Jaké znaky dokáže konkrétní QMode enkódovat
    /// </summary>
    internal enum QModeEncodeMode
    {
        /// <summary>Žádné znaky</summary>
        None,
        /// <summary>Vyjmenovanou podmnožinu znaků</summary>
        SubSet,
        /// <summary>Znaky s hodnotou 32-127</summary>
        BasicAscii,
        /// <summary>Všechny znaky</summary>
        Extended
    }
    internal enum QModeType
    {
        Terminator = 0,
        Numeric = 1,
        Alphanumeric = 2,
        StructuredAppend = 3,
        Byte = 4,
        Fnc1FirstPosition = 5,
        Eci = 7,
        Kanji = 8,
        Fnc1SecondPosition = 9
    }
    #endregion
    #region class QErrorCorrectors: tabulka všech korektorů chyb. QErrorCorrector: data jednoho korektoru chyb.
    /// <summary>
    /// Tabulka všech korektorů chyb
    /// </summary>
    internal class QErrorCorrectors : Table
    {
        #region Initiating
        internal QErrorCorrectors()
        {
            this.QErrorDict = new Dictionary<ErrorCorrectionLevel, QErrorCorrector>();
            this.QErrorDict.Add(ErrorCorrectionLevel.Low, new QErrorCorrector(ErrorCorrectionLevel.Low, 0x01, "Low"));
            this.QErrorDict.Add(ErrorCorrectionLevel.Medium, new QErrorCorrector(ErrorCorrectionLevel.Medium, 0x00, "Medium"));
            this.QErrorDict.Add(ErrorCorrectionLevel.Quartile, new QErrorCorrector(ErrorCorrectionLevel.Quartile, 0x03, "Quartile"));
            this.QErrorDict.Add(ErrorCorrectionLevel.High, new QErrorCorrector(ErrorCorrectionLevel.High, 0x02, "High"));
        }
        #endregion
        #region Data property
        /// <summary>
        /// Collection of QErrorCorrector (indexed by ErrorCorrectionLevel);
        /// </summary>
        internal Dictionary<ErrorCorrectionLevel, QErrorCorrector> QErrorDict { get; private set; }
        #endregion
    }
    /// <summary>
    /// Data jednoho korektoru chyb
    /// </summary>
    internal class QErrorCorrector : Table
    {
        #region Initiating
        internal QErrorCorrector(ErrorCorrectionLevel ecLevel, int bits, string name)
        {
            this.Level = ecLevel;
            this.Bits = bits;
            this.Name = name;
        }
        public override string ToString()
        {
            return this.Name;
        }
        #endregion
        #region Data property
        internal ErrorCorrectionLevel Level { get; private set; }
        internal int Bits { get; private set; }
        internal string Name { get; private set; }
        #endregion
    }
    #endregion
    #region class QCharSets: tabulka všech znakových sad. QCharSet: jedna znaková sada.
    /// <summary>
    /// QCharSets: tabulka všech znakových sad.
    /// </summary>
    internal class QCharSets : Table
    {
        #region Initiating
        internal QCharSets()
        {
            this._CharSetKeyDict = new Dictionary<int, QCharSet>();
            this._CharSetNameDict = new Dictionary<string, QCharSet>();
            this._AddCharSet(new QCharSet(26, "UTF-8"));                 // Has highest priority
            this._AddCharSet(new QCharSet(0, "Cp437"));
            this._AddCharSet(new QCharSet(1, "ISO8859_1", "ISO-8859-1"));
            this._AddCharSet(new QCharSet(2, "Cp437"));
            this._AddCharSet(new QCharSet(3, "ISO8859_1", "ISO-8859-1"));
            this._AddCharSet(new QCharSet(4, "ISO8859_2"));
            this._AddCharSet(new QCharSet(5, "ISO8859_3"));
            this._AddCharSet(new QCharSet(6, "ISO8859_4"));
            this._AddCharSet(new QCharSet(7, "ISO8859_5"));
            this._AddCharSet(new QCharSet(8, "ISO8859_6"));
            this._AddCharSet(new QCharSet(9, "ISO8859_7"));
            this._AddCharSet(new QCharSet(10, "ISO8859_8"));
            this._AddCharSet(new QCharSet(11, "ISO8859_9"));
            this._AddCharSet(new QCharSet(12, "ISO8859_10"));
            this._AddCharSet(new QCharSet(13, "ISO8859_11"));
            this._AddCharSet(new QCharSet(15, "ISO8859_13"));
            this._AddCharSet(new QCharSet(16, "ISO8859_14"));
            this._AddCharSet(new QCharSet(17, "ISO8859_15"));
            this._AddCharSet(new QCharSet(18, "ISO8859_16"));
            this._AddCharSet(new QCharSet(20, "SJIS", "Shift_JIS"));


            /*
Cp437 (0,2)
ISO-8859-1 (ECI codes 1,3)
ISO-8859-2 (ECI code 4)
ISO-8859-3 (ECI code 5)
ISO-8859-4 (ECI code 6)
ISO-8859-5 (ECI code 7)
ISO-8859-6 (ECI code 8)
ISO-8859-7 (ECI code 9)
ISO-8859-8 (ECI code 10)
ISO-8859-9 (ECI code 11)
ISO-8859-10 (ECI code 12)
ISO-8859-11 (ECI code 13)
ISO-8859-13 (ECI code 15)
ISO-8859-14 (ECI code 16)
ISO-8859-15 (ECI code 17)
ISO-8859-16 (ECI code 18)
Shift_JIS (ECI code 20)
Cp1250, windows-1250(ECI code 21)
Cp1251, windows-1251(ECI code 22)
Cp1252, windows-1252(ECI code 23)
Cp1256, windows-1256(ECI code 24)
UnicodeBigUnmarked, UTF-16BE, UnicodeBig (ECI code 25)
UTF-8 (ECI code 26)
US-ASCII (ECI codes 27,170)
Big5 (ECI code 28)
GB18030, GB2312, EUC_CN, GBK (ECI code 29)
EUC-KR (ECI code 30)
            */

        }
        /// <summary>
        /// Zařadí daný CharSet do indexů.
        /// Numerický musí být unikátní, názvový bere poslední výskyt stejného jména.
        /// </summary>
        /// <param name="qCharSet"></param>
        private void _AddCharSet(QCharSet qCharSet)
        {
            if (this._CharSetKeyDict.ContainsKey(qCharSet.Key))
                throw new ArgumentException("Cannot add new QCharSet with key " + qCharSet.Key + ", key already exists.");
            this._CharSetKeyDict.Add(qCharSet.Key, qCharSet);

            foreach (string name in qCharSet.Names)
            {
                if (this._CharSetNameDict.ContainsKey(name))
                    this._CharSetNameDict[name] = qCharSet;
                else
                    this._CharSetNameDict.Add(name, qCharSet);
            }
        }
        private Dictionary<int, QCharSet> _CharSetKeyDict;
        private Dictionary<string, QCharSet> _CharSetNameDict;
        #endregion
        #region Data searching
        /// <summary>
        /// Return list of QCharSet, which can encode at least one character (not IsEmpty).
        /// </summary>
        /// <param name="content"></param>
        /// <returns></returns>
        internal List<QCharSet> GetCharSetList()
        {
            return this._CharSetKeyDict.Values.Where(s => !s.IsEmpty).ToList();
        }
        /// <summary>
        /// Detect char set for mode ECI and for specified content.
        /// </summary>
        /// <param name="content"></param>
        /// <returns></returns>
        internal QCharSet DetectCharSet(string content)
        {
            return this._CharSetKeyDict[1];         // ISO-8859-1
        }
        #endregion
    }
    /// <summary>
    /// QCharSet: jedna znaková sada.
    /// </summary>
    internal class QCharSet : Table
    {
        #region Initiating
        internal QCharSet(int key, params string[] names)
        {
            this.Key = key;
            this.Names = names;
            this.CodeTable = QCodeTable.CreateForName(names[0]);
        }
        /// <summary>
        /// Vizualizace
        /// </summary>
        /// <returns></returns>
        public override string ToString()
        {
            return "Key=" + this.Key.ToString() + "; Name[0]=" + this.Names[0];
        }
        #endregion
        #region Data property
        /// <summary>
        /// Key of this charset, will be written into header buffer
        /// </summary>
        internal int Key { get; private set; }
        /// <summary>
        /// Names for this charset
        /// </summary>
        internal string[] Names { get; private set; }
        /// <summary>
        /// Code table for this charset
        /// </summary>
        internal QCodeTable CodeTable { get; private set; }
        /// <summary>
        /// true, if this QCharSet contain empty CodeTable
        /// </summary>
        internal bool IsEmpty { get { return (this.CodeTable.IsEmpty); } }
        #endregion
        #region Encoding of content
        /// <summary>
        /// Can this QCharSet encode given char?
        /// </summary>
        /// <param name="c"></param>
        /// <returns></returns>
        internal bool CanEncode(char c)
        {
            return this.CodeTable.CanEncode(c);
        }
        #endregion
        #region Encoding of header
        /// <summary>
        /// Encode pattern: this.Key (8 bit) to buffer.
        /// Does not insert ECI signature; call first ECI.EncodeHeaderToBuffer(), then QCharSet.EncodeHeaderToBuffer() !
        /// </summary>
        /// <param name="buffer"></param>
        internal void EncodeHeaderToBuffer(PatternBuffer buffer)
        {
            buffer.Append(this.Key, 8);
        }
        #endregion
    }
    #endregion
    #region class QCodeTable: encoding table from char to byte
    /// <summary>
    /// QCodeTable: encoding table from char to int (in range of 1 byte)
    /// </summary>
    internal class QCodeTable
    {
        #region Initiating and preparing codetables
        /// <summary>
        /// Create and return a charset for codes 0x20 ÷ 0x7E = basic ASCII codetable
        /// </summary>
        /// <returns></returns>
        internal static QCodeTable CreateBasicASCII()
        {
            QCodeTable ct = new QCodeTable();
            for (int code = 0x20; code < 0x7F; code++)
                ct._CharacterTable.Add((char)code, code);
            return ct;
        }
        /// <summary>
        /// Create and return a charset for specified characters, where value for each character is determined by its position in definition string.
        /// </summary>
        /// <returns></returns>
        internal static QCodeTable CreateExplicitTable(string definition, int firstCode)
        {
            QCodeTable ct = new QCodeTable();
            ct._FillTable(definition, firstCode, true);
            return ct;
        }
        /// <summary>
        /// Create and return a charset for codes 0xA0 ÷ 0xFF = extended characters, for specified encoding
        /// </summary>
        /// <param name="encodingName"></param>
        /// <returns></returns>
        internal static QCodeTable CreateForName(string encodingName)
        {
            QCodeTable ct = new QCodeTable();
            if (encodingName == "UTF-8")
                ct._IsUtf8 = true;
            else
            {
                string definition = _GetExtendedCharacterSet(encodingName);
                ct._FillTable(definition, 0xA0, true);
            }
            return ct;
        }
        /// <summary>
        /// Fill the private dictionary this._CharacterTable with indexes for characters specific for this encoding.
        /// </summary>
        /// <param name="ctDef"></param>
        /// <param name="firstCode"></param>
        private void _FillTable(string ctDef, int firstCode, bool spaceIsOnly0x20)
        {
            for (int i = 0; i < ctDef.Length; i++)
            {
                char c = ctDef[i];
                int code = firstCode + i;
                if (c == ' ' && spaceIsOnly0x20 &&code != 0x20)
                    continue;
                if (this._CharacterTable.ContainsKey(c))
                    throw new FormatException("QCodeTable: character '" + c + "' (=0x" + ((int)c).ToString("X4") + " is duplicite.");
                this._CharacterTable.Add(c, code);
            }
        }
        /// <summary>
        /// Constructor
        /// </summary>
        private QCodeTable()
        {
            this._CharacterTable = new Dictionary<char, int>();
        }
        /// <summary>
        /// Visualisation
        /// </summary>
        /// <returns></returns>
        public override string ToString()
        {
            return this.Name;
        }
        /// <summary>
        /// Name of this encoding
        /// </summary>
        internal string Name { get; private set; }
        /// <summary>
        /// Index if char and their code values. Key is a char, value is number (0 ÷ 255), representing this char in current charset encoding.
        /// </summary>
        private Dictionary<char, int> _CharacterTable;
        private bool _IsUtf8;
        /// <summary>
        /// true, if this CodeTable is empty (none of char can be encoded)
        /// </summary>
        internal bool IsEmpty { get { return (!this._IsUtf8 && this._CharacterTable.Count == 0); } }

        private static string _GetExtendedCharacterSet(string encodingName)
        {
            switch (encodingName)
            {   // For name of encoding return a 96-char len string, containing char (in .NET UTF16) for specified table char 0xA0 ÷ 0xFF:
                // If return value is "", then encoding does not implemented.
                // If any char in string is SPACE, then corresponding code is not implemented.
                case "Cp437": return "";
                case "ISO-8859-1":
                case "ISO8859_1": return " ¡¢£¤¥¦§¨©ª«¬ ®¯°±²³´µ¶·¸¹º»¼½¾¿ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖ×ØÙÚÛÜÝÞßàáâãäåæçèéêëìíîïðñòóôõö÷øùúûüýþÿ";
                case "ISO8859_2": return " Ą˘Ł¤ĽŚ§¨ŠŞŤŹ ŽŻ°ą˛ł´ľśˇ¸šşťź˝žżŔÁÂĂÄĹĆÇČÉĘËĚÍÎĎĐŃŇÓÔŐÖ×ŘŮÚŰÜÝŢßŕáâăäĺćçčéęëěíîďđńňóôőö÷řůúűüýţ˙";
                case "ISO8859_3": return " Ħ˘£¤ Ĥ§¨İŞĞĴ  Ż°ħ²³´µĥ·¸ışğĵ½ żÀÁÂ ÄĊĈÇÈÉÊËÌÍÎÏ ÑÒÓÔĠÖ×ĜÙÚÛÜŬŜßàáâ äċĉçèéêëìíîï ñòóôġö÷ĝùúûüŭŝ˙";
                case "ISO8859_4":
                case "ISO8859_5":
                case "ISO8859_6":
                case "ISO8859_7":
                case "ISO8859_8":
                case "ISO8859_9":
                case "ISO8859_10":
                case "ISO8859_11":
                case "ISO8859_12":
                case "ISO8859_13":
                case "ISO8859_14":
                case "ISO8859_15":
                case "ISO8859_16":
                case "SJIS":
                case "Shift_JIS":
                    return "";
            }
            return "";
        }
        #endregion
        #region Encoding
        /// <summary>
        /// Returns true, if this encoding can encode specified character
        /// </summary>
        /// <param name="c"></param>
        /// <returns></returns>
        internal bool CanEncode(char c)
        {
            if (this._IsUtf8) return true;
            return (this._CharacterTable.ContainsKey(c));
        }
        /// <summary>
        /// Returns code (numeric) for specified character.
        /// Throw InvalidProgramException if character does not exists in this table.
        /// </summary>
        /// <param name="c"></param>
        /// <returns></returns>
        internal int Encode(char c)
        {
            int code;
            if (this._CharacterTable.TryGetValue(c, out code)) return code;
            throw new InvalidProgramException("Attempt to encode");
        }
        /// <summary>
        /// Encode given character to buffer
        /// </summary>
        /// <param name="c"></param>
        /// <param name="buffer"></param>
        /// <returns></returns>
        internal void EncodeToBuffer(char c, PatternBuffer buffer)
        {
            if (this._IsUtf8)
            {
                byte[] code = System.Text.Encoding.UTF8.GetBytes(c.ToString());
                int l = code.Length;
                foreach (int value in code)
                    buffer.Append(value, 8);
            }
            else
            {
                int value = this.Encode(c);
                buffer.Append(value, 8);
            }
        }
        #endregion
    }
    #endregion
    #region class QPatterns: tabulka všech vizuálních vzorů. QPattern: jeden vizuální vzor.
    /// <summary>
    /// QPatterns: tabulka všech vizuálních vzorů. QPattern: jeden vizuální vzor.
    /// </summary>
    internal class QPatterns: Table
    {
        #region Initiating
        /// <summary>
        /// Vytvoří sadu vzorů.
        /// </summary>
        internal QPatterns()
        {
            this.PatternDict = new Dictionary<QPatternType, QPattern>();
            this._AddPattern(QPatternType.PositionDetection, "1111111;1000001;1011101;1011101;1011101;1000001;1111111");     // big square in corners
            this._AddPattern(QPatternType.PositionAdjustment, "11111;10001;10101;10001;11111");                              // small squares in area, by version
            this._AddPattern(QPatternType.HorizontalSeparation, "00000000");                                                 // horizontal separator 8 pixel
            this._AddPattern(QPatternType.VerticalSeparation, "0;0;0;0;0;0;0");                                              // vertical separator 7 pixel

            
        }
        /// <summary>
        /// Add one pattern
        /// </summary>
        /// <param name="patternType"></param>
        /// <param name="data"></param>
        private void _AddPattern(QPatternType patternType, string data)
        {
            this.PatternDict.Add(patternType, new QPattern(patternType, data));
        }
        #endregion
        #region Data property
        internal Dictionary<QPatternType, QPattern> PatternDict { get; private set; }
        #endregion
    }
    /// <summary>
    /// QPattern: Specifikace jednoho vzoru.
    /// </summary>
    internal class QPattern : Table
    {
        #region Initiating
        internal QPattern(QPatternType patternType, string data)
        {
            this.PatternType = patternType;
            this.Data = data;
            this.Matrix = new CodeMatrix(data);
        }
        /// <summary>
        /// Vizualizace
        /// </summary>
        /// <returns></returns>
        public override string ToString()
        {
            return "PatternType=" + this.PatternType.ToString();
        }
        #endregion
        #region Data property
        internal QPatternType PatternType { get; private set; }
        internal string Data { get; private set; }
        internal CodeMatrix Matrix { get; private set; }
        #endregion
    }
    internal enum QPatternType
    {
        None,
        PositionDetection,
        HorizontalSeparation,
        VerticalSeparation,
        PositionAdjustment,

    }
    #endregion
    #region class Table
    internal class Table
    {
        internal Table()
        {
        }
        protected int[] Parse(string value)
        {
            return Parse(value, ';');
        }
        protected int[] Parse(string value, char delimiter)
        {
            if (String.IsNullOrEmpty(value)) return new int[0];
            if (value.Trim().Length == 0) return new int[0];

            char[] separators = new char[] { delimiter };
            string[] items = value.Split(separators, StringSplitOptions.None);
            int[] result = new int[items.Length];
            for (int i = 0; i < items.Length; i++)
            {
                int n;
                if (Int32.TryParse(items[i], out n))
                    result[i] = n;
            }
            return result;
        }
    }
    #endregion
}
